package user

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"ruoyi-vue-pro-go/api/common"
	v1 "ruoyi-vue-pro-go/api/user/v1"
	"ruoyi-vue-pro-go/internal/consts"
	"ruoyi-vue-pro-go/internal/dao"
	"ruoyi-vue-pro-go/internal/model/do"
	"ruoyi-vue-pro-go/internal/model/entity"
	"ruoyi-vue-pro-go/internal/model/vo"
	"ruoyi-vue-pro-go/internal/service"
	"ruoyi-vue-pro-go/utility/auth"
	"ruoyi-vue-pro-go/utility/excel"
)

type (
	sUser struct{}
)

func init() {
	service.RegisterUser(New())
}

func New() service.IUser {
	return &sUser{}
}

func (s *sUser) GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error) {
	res = &v1.GetOneRes{}
	err = dao.SystemUsers.Ctx(ctx).Where(do.SystemUsers{
		Id: req.Id,
	}).Scan(&res.SystemUserVO)
	return
}

func (s *sUser) GetList(ctx context.Context, req *v1.GetListReq) (res *v1.GetListRes, err error) {
	res = &v1.GetListRes{}
	err = dao.SystemUsers.Ctx(ctx).Scan(&res.List)
	return
}

func (s *sUser) GetOneByName(ctx context.Context, req *v1.GetOneByNameReq) (res *v1.GetOneByNameRes, err error) {
	res = &v1.GetOneByNameRes{}
	err = dao.SystemUsers.Ctx(ctx).Where(do.SystemUsers{
		Username: req.Username,
	}).Scan(&res.SystemUserVO)
	return
}

func (s *sUser) GetPage(ctx context.Context, req *v1.GetPageReq) (res *v1.GetPageRes, err error) {
	res = &v1.GetPageRes{
		List:          make([]*vo.SystemUserVO, 0),
		CommonPageRes: &common.CommonPageRes{},
	}
	model := dao.SystemUsers.Ctx(ctx)
	if req.Username != "" {
		model = model.WhereLike("name", "%"+req.Username+"%")
	}
	if req.Mobile != "" {
		model = model.WhereLike("mobile", "%"+req.Mobile+"%")
	}
	if req.DeptId != -1 {
		model = model.Where("dept_id", req.DeptId).LeftJoin("system_dept d", "d.id = system_users.dept_id").
			Fields("system_users.*, d.name as dept_name")
	}
	// 添加时间范围条件
	if len(req.CreateTime) != 0 {
		beginTime := gtime.New(req.CreateTime[0])
		endTime := gtime.New(req.CreateTime[1])
		if beginTime != nil && endTime != nil {
			// 验证时间顺序
			if beginTime.Time.After(endTime.Time) {
				return nil, gerror.New("开始时间不能晚于结束时间")
			}

			model = model.WhereBetween("create_time", beginTime, endTime)
		} else if beginTime != nil {
			model = model.WhereGTE("create_time", beginTime)
		} else if endTime != nil {
			model = model.WhereLTE("create_time", endTime)
		}
	}
	r := ghttp.RequestFromCtx(ctx)
	tenantId := gconv.Int64(r.Header.Get("Tenant-Id"))
	model = model.Where(do.SystemUsers{
		TenantId: tenantId,
	})
	if req.PageSize != 0 {
		res.PageNo = req.PageNo
		res.PageSize = req.PageSize
		model = model.Page(req.PageNo, req.PageSize)
	}
	err = model.ScanAndCount(&res.List, &res.Total, false)
	return
}

func (s *sUser) GetPermissionListByUserId(ctx context.Context, req *v1.GetPermissionListByUserIdReq) (res *v1.GetPermissionListByUserIdRes, err error) {
	array, err := g.DB().Model("system_user_role ur").
		LeftJoin("system_role_menu rm", "ur.role_id = rm.role_id").
		LeftJoin("system_menu m", "rm.menu_id = m.id").
		Where("ur.user_id", req.UserId).
		Fields("m.permission").Where("m.deleted = ?", 0).Array()
	if err != nil {
		return nil, err
	}
	var permissions []string
	for _, v := range array {
		if gconv.String(v) != "" {
			permissions = append(permissions, gconv.String(v))
		}
	}
	go func() {
		_, err = g.Redis().Set(ctx, fmt.Sprintf("user_permissions:%d", req.UserId), permissions)
		if err != nil {
			g.Log().Debug(ctx, "写入用户权限列表缓存失败")
		}
	}()
	res = &v1.GetPermissionListByUserIdRes{
		List: permissions,
	}
	return
}

func (s *sUser) GetRoleListByUserId(ctx context.Context, req *v1.GetRoleListByUserIdReq) (res *v1.GetRoleListByUserIdRes, err error) {
	var roles []string
	err = g.DB().Model("system_user_role ur").
		LeftJoin("system_role r", "ur.role_id = r.id").
		Where("ur.user_id", req.UserId).
		Fields("r.code").Where("r.deleted = ?", 0).
		Scan(&roles)
	if err != nil {
		return nil, err
	}
	res = &v1.GetRoleListByUserIdRes{
		List: roles,
	}
	return
}

func (s *sUser) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error) {
	res = &v1.UpdateRes{}
	// 这里需要获取当前用户名和租户id
	r := ghttp.RequestFromCtx(ctx)
	username := r.GetCtxVar("username").String()
	tenantId := gconv.Int64(r.Header.Get("Tenant-Id"))
	req.TenantId = tenantId
	req.Creator = username
	req.Updater = username
	_, err = dao.SystemUsers.Ctx(ctx).Data(req).Where(do.SystemUsers{
		Id: req.Id,
	}).Update()
	if err != nil {
		return nil, gerror.NewCode(gcode.CodeDbOperationError, "user update failed")
	}
	return
}

func (s *sUser) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) {
	res = &v1.CreateRes{}
	// 这里需要获取当前用户名和租户id
	r := ghttp.RequestFromCtx(ctx)
	username := r.GetCtxVar("username").String()
	tenantId := gconv.Int64(r.Header.Get("Tenant-Id"))
	req.TenantId = tenantId
	req.Creator = username
	req.Updater = username
	var cls = dao.SystemUsers.Columns()
	count, err := dao.SystemUsers.Ctx(ctx).
		Where(cls.Username, req.Username).Count()
	if err != nil {
		return res, err
	}
	if count > 0 {
		return res, gerror.NewCode(gcode.CodeDbOperationError, "用户已存在")
	}
	_, err = dao.SystemUsers.Ctx(ctx).Data(req).Insert()
	if err != nil {
		return res, gerror.NewCode(gcode.CodeDbOperationError, "user create failed")
	}
	return
}

func (s *sUser) Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error) {
	res = &v1.DeleteRes{}
	var cls = dao.SystemUsers.Columns()
	_, err = dao.SystemUsers.Ctx(ctx).Where(cls.Id, req.Id).Delete()
	if err != nil {
		return nil, gerror.NewCode(gcode.CodeDbOperationError, "user delete failed")
	}
	// 删除数据库用户岗位
	go func() {
		_, err = dao.SystemUserPost.Ctx(ctx).Where("user_id", req.Id).Delete()
		if err != nil {
			g.Log().Debug(ctx, "清除用户岗位数据库失败")
		}
	}()
	// 清除用户权限缓存
	go func() {
		_, err = g.Redis().Del(ctx, fmt.Sprintf("user_permissions:%d", req.Id))
		if err != nil {
			g.Log().Debug(ctx, "清除用户权限缓存失败")
		}
	}()
	// 清除用户角色缓存
	go func() {
		_, err = g.Redis().Del(ctx, fmt.Sprintf("user_roles:%d", req.Id))
		if err != nil {
			g.Log().Debug(ctx, "清除用户角色缓存失败")
		}
	}()
	return
}

func (s *sUser) DeleteList(ctx context.Context, req *v1.DeleteListReq) (res *v1.DeleteListRes, err error) {
	res = &v1.DeleteListRes{}
	var cls = dao.SystemUsers.Columns()
	// 参数转换
	ids := gconv.Ints(gstr.Split(req.Ids, ","))
	if len(ids) == 0 {
		return nil, gerror.New("无效的ID参数")
	}
	_, err = dao.SystemUsers.Ctx(ctx).WhereIn(cls.Id, ids).Delete()
	if err != nil {
		return nil, gerror.NewCode(gcode.CodeDbOperationError, "user delete list failed")
	}
	// 删除数据库用户权限
	go func() {
		for _, id := range ids {
			_, err = dao.SystemUserRole.Ctx(ctx).Where("user_id", id).Delete()
			if err != nil {
				g.Log().Debug(ctx, "清除用户角色数据库失败")
			}
		}
	}()
	// 删除数据库用户岗位
	go func() {
		for _, id := range ids {
			_, err = dao.SystemUserPost.Ctx(ctx).Where("user_id", id).Delete()
			if err != nil {
				g.Log().Debug(ctx, "清除用户岗位数据库失败")
			}
		}
	}()
	// 清除用户权限缓存
	go func() {
		for _, id := range ids {
			_, err = g.Redis().Del(ctx, fmt.Sprintf("user_permissions:%d", id))
			if err != nil {
				g.Log().Debug(ctx, "清除用户权限缓存失败")
			}
		}
	}()
	// 清除用户角色缓存
	go func() {
		for _, id := range ids {
			_, err = g.Redis().Del(ctx, fmt.Sprintf("user_roles:%d", id))
			if err != nil {
				g.Log().Debug(ctx, "清除用户角色缓存失败")
			}
		}
	}()
	return
}

func (s *sUser) ExportExcel(ctx context.Context, req *v1.ExportExcelReq) (res *v1.ExportExcelRes, err error) {
	res = &v1.ExportExcelRes{}
	// 1. 获取用户数据
	userList, err := service.User().GetList(ctx, &v1.GetListReq{})
	if err != nil {
		return nil, gerror.Wrap(err, "获取用户数据失败")
	}

	// 2. 创建导出器
	exporter := excel.NewGenericExporter(
		"用户列表.xlsx",
		userList.List,
		excel.WithFields(consts.UserExportFieldsOrder...),
		excel.WithHeaderMap(consts.UserExportFields),
	)

	// 3. 执行导出
	r := ghttp.RequestFromCtx(ctx)
	if err := exporter.Export(ctx, r.Response); err != nil {
		return nil, gerror.Wrap(err, "导出用户列表失败")
	}

	// 4. 终止后续处理
	r.ExitAll()
	return nil, nil
}

func (s *sUser) DownLoadTemplate(ctx context.Context, req *v1.DownloadTemplateReq) (res *v1.DownloadTemplateRes, err error) {
	res = &v1.DownloadTemplateRes{}
	// 1. 创建示例数据（空数据，只包含表头）
	var sampleUsers []*entity.SystemUsers

	// 2. 创建导出器 - 使用与导入匹配的字段映射
	exporter := excel.NewGenericExporter(
		"用户导入模板.xlsx",
		sampleUsers,
		excel.WithFields(consts.UserTemplateFieldsOrder...),
		excel.WithHeaderMap(consts.UserTemplateFields),
	)

	// 3. 设置响应
	r := ghttp.RequestFromCtx(ctx)
	if err := exporter.Export(ctx, r.Response); err != nil {
		return nil, gerror.Wrap(err, "下载用户导入模板失败")
	}

	// 4. 终止后续处理
	r.ExitAll()
	return
}

func (s *sUser) ImportExcel(ctx context.Context, req *v1.ImportExcelReq) (res *v1.ImportExcelRes, err error) {
	res = &v1.ImportExcelRes{}
	// 1. 获取上传的文件
	file, err := req.File.Open()
	if err != nil {
		return nil, gerror.Wrap(err, "打开上传文件失败")
	}
	defer file.Close()

	// 2. 准备接收导入数据的容器
	var users []*entity.SystemUsers

	// 3. 创建导入器并配置
	importer := excel.NewGenericImporter(
		excel.WithFieldMap(consts.UserImportFields),
		excel.WithRequiredFields("Username", "Nickname", "Sex", "Status"),
		excel.WithValidator("Email", func(v interface{}) error {
			email := gconv.String(v)
			if email != "" {
				if err := g.Validator().Rules("email").Data(email).Run(ctx); err != nil {
					return gerror.New("邮箱格式不正确")
				}
			}
			return nil
		}),
		excel.WithTransformer("Sex", func(v interface{}) (interface{}, error) {
			if gconv.String(v) == "" {
				return 0, nil
			}
			return gconv.Int(v), nil
		}),
		excel.WithTransformer("Status", func(v interface{}) (interface{}, error) {
			if gconv.String(v) == "" {
				return 0, nil
			}
			return gconv.Int(v), nil
		}),
	)

	// 4. 执行导入
	importErrors, err := importer.Import(ctx, file, &users)
	if err != nil {
		return nil, gerror.Wrap(err, "导入Excel失败")
	}
	// 这里需要获取当前用户名和租户id
	r := ghttp.RequestFromCtx(ctx)
	username := r.GetCtxVar("username").String()
	tenantId := gconv.Int64(r.Header.Get("Tenant-Id"))
	for _, user := range users {
		user.Creator = username
		user.Updater = username
		user.TenantId = tenantId
		curTime := gtime.Now()
		user.CreateTime = curTime
		user.UpdateTime = curTime
	}

	// 5. 处理导入结果
	if len(importErrors) > 0 {
		errorDetails := make([]map[string]interface{}, len(importErrors))
		for i, e := range importErrors {
			var errMsgs []string
			for _, err := range e.Errors {
				errMsgs = append(errMsgs, err.Error())
			}
			errorDetails[i] = map[string]interface{}{
				"row":    e.Row,
				"errors": errMsgs,
			}
		}

		return &v1.ImportExcelRes{
			Code:    -1,
			Message: "部分数据校验失败",
			Data: map[string]interface{}{
				"successCount": len(users),
				"errorCount":   len(importErrors),
				"errors":       errorDetails,
			},
		}, nil
	}

	// 6. 保存有效数据到数据库
	// 批量插入或更新
	pdateSupport := req.UpdateSupport
	if pdateSupport {
		_, err = dao.SystemUsers.Ctx(ctx).OnConflict("username").Save(users)
		if err != nil {
			g.Log().Error(ctx, err)
			return
		}
		g.Log().Info(ctx, "批量插入/更新成功")
		return
	} else {
		_, err = dao.SystemUsers.Ctx(ctx).Insert(users)
		if err != nil {
			g.Log().Error(ctx, err)
			return
		}
		g.Log().Info(ctx, "批量插入/更新成功")
	}
	return
}

func (s *sUser) ResetPassword(ctx context.Context, req *v1.ResetPasswordReq) (res *v1.ResetPasswordRes, err error) {
	res = &v1.ResetPasswordRes{}
	user, err := service.User().GetOne(ctx, &v1.GetOneReq{
		Id: req.Id,
	})
	if user == nil {
		return nil, gerror.New("用户不存在")
	}
	newpassword, err := auth.EncryptPassword(req.Password)
	if err != nil {
		return nil, gerror.Wrap(err, "加密密码失败")
	}
	_, err = dao.SystemUsers.Ctx(ctx).Where(do.SystemUsers{
		Id: req.Id,
	}).Update(do.SystemUsers{
		Password: newpassword,
	})
	if err != nil {
		return nil, gerror.Wrap(err, "更新密码失败")
	}
	return
}
